﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ThiefRun.Character;
using Microsoft.Xna.Framework;

namespace ThiefRun.Behaviour
{
    public static class SteeringBehaviour
    {
        #region Methods
        #region Public Methods
        // Seek the target using the Steering Seek method.
        public static void Seek(ref MovementProperties linear, ref MovementProperties targetLinear, AIProperties aiProperties)
        {
            // stop
            linear.velocity = Vector2.Zero;

            // Find the acceleration direction and calculate the acceleration.
            Vector2 deltaPosition = targetLinear.position - linear.position;
            deltaPosition.Normalize();

            linear.acceleration = aiProperties.maxLinear.acceleration * deltaPosition;
            
            // Calculate the velocity taking to ensure that it does not exceed the maximum.
            linear.velocity += linear.acceleration;
            linear.velocity = aiProperties.maxLinear.velocity * (linear.velocity / linear.velocity.Length());

            // Calculate the position.
            linear.position += linear.velocity;
        }

        // Align with the direction of motion.
        public static void Align(MovementProperties linear, ref MovementProperties angular,  AIProperties aiProperties)
        {
            // Find the desired direction.
            Vector2 normalizedVelocity = linear.velocity;
            normalizedVelocity.Normalize();
            Vector2 desiredDirection = new Vector2((float)Math.Acos(normalizedVelocity.X), (float)Math.Asin(normalizedVelocity.Y));

            // Calculate the angular acceleration.
            Vector2 deltaDirection = desiredDirection - angular.position;
            deltaDirection.Normalize();

            angular.acceleration = aiProperties.maxAngular.acceleration * deltaDirection;            

            // Calculate the velocity taking to ensure that it does not exceed the maximum.
            angular.velocity += angular.acceleration;
            angular.velocity = aiProperties.maxAngular.velocity * (angular.velocity / angular.velocity.Length());

            // Calculate the position.
            angular.position += angular.velocity;
        }
        #endregion Public Methods

        #region Private Methods
        /*private void SteeringArrive()
        {
            // Use Pursue for Q3 only.
            if (game.QMode() == Game1.QModes.Q3)
            {
                Pursue();
            }
            else
            {
                // The character is stationary or moving very slowly (A)
                if ((linear.velocity == Vector3.Zero) || (linear.velocity.Length() <= SLOW_VELOCITY))
                {
                    // small distance from target (Ai)
                    if (nearSphere.Intersects(target.boundingSphere))
                    {
                        // step there directly, even if this involves moving backward or sidestepping

                        // Find the optimal velocity.
                        Vector3 goalVelocity = target.linear.position - linear.position;

                        linear.acceleration.X = Math.Min(MAXIMUM_ACCELERATION, (goalVelocity.X - linear.velocity.X) / TIME_TO_TARGET);
                        linear.acceleration.Z = Math.Min(MAXIMUM_ACCELERATION, (goalVelocity.Z - linear.velocity.Z) / TIME_TO_TARGET);
                        linear.velocity += linear.acceleration;
                        linear.position += linear.velocity;
                    }
                    // far from target (Aii)
                    else
                    {
                        Vector3 deltaPosition = target.linear.position - linear.position;
                        deltaPosition.Normalize();

                        linear.acceleration = MAXIMUM_ACCELERATION * deltaPosition;
                        linear.velocity += linear.acceleration;
                        linear.velocity.X = Math.Min(MaxVelocity(), linear.velocity.X);
                        linear.velocity.Z = Math.Min(MaxVelocity(), linear.velocity.Z);
                        linear.position += linear.velocity;

                        // TODO: Change Direction
                        //position += currentVelocity;
                        // turn on the spot towards target
                        /*direction.Y += ((target.direction.Y + MathHelper.ToRadians(180)) - direction.Y) / TIME_TO_ALIGN;

                        // move towards target
                        velocity.X = MAXIMUM_VELOCITY;
                        velocity.Z = MAXIMUM_VELOCITY;
                        position += currentVelocity;
                    }
                }
                else
                {
                    /*cone = new Cone(MathHelper.ToRadians(perceptionAngle), PERCEPTION_HEIGHT);
                    cone.Generate();


                    // if in speed dependent arc (cone of perception) and in front (Bi).
                    if (false)
                    {
                        // move forward with rotation.
                    }
                    // Target outside cone of perception (Bii)
                    else
                    {
                        Vector3 deltaPosition = target.linear.position - linear.position;
                        deltaPosition.Normalize();

                        // stop
                        linear.velocity = Vector3.Zero;

                        // turn on the spot towards target
                        Face();

                        linear.acceleration = maximumAcceleration * deltaPosition;
                        linear.velocity += linear.acceleration;
                        linear.velocity.X = Math.Min(MaxVelocity(), linear.velocity.X);
                        linear.velocity.Z = Math.Min(MaxVelocity(), linear.velocity.Z);

                        linear.position += linear.velocity;
                    }
                }
            }
        }*/

        /*private void SteeringEvade()
        {
            // small distance from target (Ci)
            if (nearSphere.Intersects(target.boundingSphere))
            {
                // step there directly, even if this involves moving backward or sidestepping

                // Find the optimal velocity.
                Vector3 goalVelocity = linear.position - target.linear.position;

                linear.acceleration.X = Math.Min(MAXIMUM_ACCELERATION, (goalVelocity.X - linear.velocity.X) / TIME_TO_TARGET);
                linear.acceleration.Z = Math.Min(MAXIMUM_ACCELERATION, (goalVelocity.Z - linear.velocity.Z) / TIME_TO_TARGET);
                linear.velocity += linear.acceleration;
                linear.position += linear.velocity;
            }
            // far from target (Cii)
            else
            {
                Vector3 deltaPosition = linear.position - target.linear.position;
                deltaPosition.Normalize();

                // stop
                linear.velocity = Vector3.Zero;

                // turn on the spot towards target
                FaceAway();

                linear.acceleration = maximumAcceleration * deltaPosition;
                linear.velocity += linear.acceleration;
                linear.velocity.X = Math.Min(MaxVelocity(), linear.velocity.X);
                linear.velocity.Z = Math.Min(MaxVelocity(), linear.velocity.Z);

                linear.position += linear.velocity;
            }
        }
        private void SteeringSeek()
        {
          // Need Align
        }

        private void Pursue()
        {
            float predictedTime = ((target.linear.position - linear.position).Length()) / MaxVelocity();

            SteeringSeek();
        }*/
        #endregion Private Methods
        #endregion Methods
    }
}
